/******************************************************************************
* echo_server.c                                                               *
*                                                                             *
* Description: This file contains the C source code for an echo server.  The  *
*              server runs on a hard-coded port and simply write back anything*
*              sent to it by connected clients.  It does not support          *
*              concurrent clients.                                            *
* 特点：绑定在固定端口上、接收到客户端消息就原样返回、不支持并发                                                                            *
* Authors: Athula Balachandran <abalacha@cs.cmu.edu>,                         *
*          Wolf Richter <wolf@cs.cmu.edu>                                     *
*                                                                             *
*******************************************************************************/

#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define ECHO_PORT 9999// 服务器绑定的端口
#define BUF_SIZE 4096

//socket关闭函数
int close_socket(int sock)
{
    if (close(sock))
    {
        fprintf(stderr, "Failed closing socket.\n");
        return 1;
    }
    return 0;
}

int is_valid_request(const char* request_buf) {
    // 检查请求是否太短
    if (strlen(request_buf) < 14) return 0;
   // 划分请求行
    const char* line_end = strstr(request_buf, "\r\n");
    if (line_end == NULL) return 0;
    char request_line[BUF_SIZE];
    size_t line_len = line_end - request_buf;
    if (line_len >= BUF_SIZE) return 0;
    strncpy(request_line, request_buf, line_len);
    request_line[line_len] = '\0';
    // 检查是否有前导空格
    if (request_line[0] == ' ' || request_line[0] == '\t') return 0;
    // 检查请求行是否完整包含方法、路径和HTTP版本三个元素
    char method[16], uri[BUF_SIZE], version[16];
    int parsed = sscanf(request_line, "%15s %255s %63s", method, uri, version);
    if (parsed != 3) return 0;
    // 检査 HTTP 版本是否合法
    if(strcmp(version, "HTTP/1.0")!=0 && strcmp(version, "HTTP/1.1")!= 0){
        return 0;
    }
    // 划分请求头
    const char* header_start = line_end + 2;// 跳过第一个\r\n
    const char* request_end = strstr(request_buf, "\r\n\r\n");
    if (request_end == NULL) return 0;// 没有结束标志，无效
    char headers[BUF_SIZE];
    size_t headers_len = request_end - header_start;
    if (headers_len >= BUF_SIZE) return 0;
    strncpy(headers, header_start, headers_len);
    headers[headers_len] = '\0';
    // 如果有头部，检查每一行的格式
    if (headers_len > 0) {
        char* line = headers;
        while (line && *line) {
            char* next_line = strstr(line, "\r\n");
            if (next_line) {
                *next_line = '\0';
                next_line += 2;
            }
            char* colon = strchr(line, ':');
            if (colon == NULL || colon == line || *(colon + 1) != ' ') {
                return 0;
            }
            line = next_line;
        }
    }
    if (strcmp(method, "GET") == 0 || strcmp(method, "HEAD") == 0 || strcmp(method, "POST") == 0) return 1;  
    return 2;
}

int main(int argc, char* argv[])
{
    int sock, client_sock;              // 服务器 socket 和 客户端 socket
    ssize_t readret;                    // 接收数据长度
    socklen_t cli_size;                // 客户端地址结构大小
    struct sockaddr_in addr, cli_addr; // 服务器和客户端地址结构
    //char buf[BUF_SIZE];                // 消息缓冲区

    fprintf(stdout, "----- Echo Server -----\n");
    
    //创建 socket
    /* all networked programs must create a socket */
    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        fprintf(stderr, "Failed creating socket.\n");
        return EXIT_FAILURE;
    }

    //填充本地地址信息并绑定
    addr.sin_family = AF_INET;
    addr.sin_port = htons(ECHO_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    /* servers bind sockets to ports---notify the OS they accept connections */
    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)))
    {
        close_socket(sock);
        fprintf(stderr, "Failed binding socket.\n");
        return EXIT_FAILURE;
    }

    //监听端口
    if (listen(sock, 5))
    {
        close_socket(sock);
        fprintf(stderr, "Error listening on socket.\n");
        return EXIT_FAILURE;
    }

    //主循环接收客户端连接
    /* finally, loop waiting for input and then write it back */
    while (1)//两个 while 循环，作用是分层处理：一个处理“客户端连接”，一个处理“客户端发送的数据”。
    {
        cli_size = sizeof(cli_addr);
        if ((client_sock = accept(sock, (struct sockaddr *) &cli_addr,
                                    &cli_size)) == -1)//接收客户端连接
        {
            close(sock);
            fprintf(stderr, "Error accepting connection.\n");//没连上
            return EXIT_FAILURE;
        }

        readret = 0;
        char recv_buf[BUF_SIZE];
        int recv_len = 0;

        // 接收并处理多个请求
        while ((readret = recv(client_sock, recv_buf, BUF_SIZE, 0)) >= 1) {
            recv_len += readret;
            recv_buf[recv_len] = '\0'; // null 结尾，方便字符串处理

            char *start = recv_buf;
            while (1) {//在一个输入内，循环输入内的多个请求分别进行响应
                if (start >= recv_buf + recv_len)
                    break; // 指针已经超过接收缓冲区的内容范围，退出循环

                //提取单个请求用于独立解析
                char *end_of_request = strstr(start, "\r\n\r\n"); // 查找请求结束标志 "\r\n\r\n"是一个请求的结束标志
                if (!end_of_request)
                    break; // 请求未接收完整，跳出等待更多数据

                end_of_request += 4; // 把指针移动到请求报文的末尾（即 \r\n\r\n 之后的位置），指向下一个请求的开头
                int request_len = end_of_request - start;//计算这个请求的总长度

                char request_buf[BUF_SIZE];//声明一个新的缓冲区 request，用来保存这单个完整的请求
                strncpy(request_buf, start, request_len);//把单个从 recv_buf 复制出来，便于独立处理这一个请求。
                request_buf[request_len] = '\0';//加一个结尾标志\0

                // 使用增强的请求验证
                int flag = is_valid_request(request_buf);
                if (flag==0) {//请求格式有误
                    const char *bad_request = "HTTP/1.1 400 Bad Request\r\n\r\n";
                    send(client_sock, bad_request, strlen(bad_request), 0);
                    start = end_of_request;
                    continue;
                } else if (flag == 2) {//请求的指令没有实现
                    const char *not_impl = "HTTP/1.1 501 Not Implemented\r\n\r\n";
                    send(client_sock, not_impl, strlen(not_impl), 0);
                    start = end_of_request;
                    continue;
                }

                //合法请求，回显内容
                ssize_t m = send(client_sock, request_buf, request_len, 0);//send()：原样将客户端的输入回传
                if (m != request_len) {
                    close_socket(client_sock);
                    close_socket(sock);
                    fprintf(stderr, "Error sending to client.\n");
                    start = end_of_request;
                    return EXIT_FAILURE;
                }
                //移动指针到下一个请求
                start = end_of_request;
            }

            // 将未处理的数据移到缓冲区前部
            int remaining = recv_buf + recv_len - start;
            memmove(recv_buf, start, remaining);
            recv_len = remaining;
        } 

        if (readret == -1)//如果 recv() 返回 -1，说明读取出错。
        {
            close_socket(client_sock);
            close_socket(sock);
            fprintf(stderr, "Error reading from client socket.\n");
            return EXIT_FAILURE;
        }

        if (close_socket(client_sock))//关闭客户端 socket
        {
            close_socket(sock);
            fprintf(stderr, "Error closing client socket.\n");
            return EXIT_FAILURE;
        }
    }

    close_socket(sock);

    return EXIT_SUCCESS;
}
